package com.wenzhoujie.library.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;

import com.wenzhoujie.library.R;

public class SlidingDrawer extends ViewGroup
{
	public static final int ORIENTATION_HORIZONTAL = 0;
	public static final int ORIENTATION_VERTICAL = 1;

	private static final int TAP_THRESHOLD = 6;
	private static final float MAXIMUM_TAP_VELOCITY = 100.0f;
	private static final float MAXIMUM_MINOR_VELOCITY = 150.0f;
	private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
	private static final float MAXIMUM_ACCELERATION = 2000.0f;
	private static final int VELOCITY_UNITS = 1000;
	private static final int MSG_ANIMATE = 1000;
	private static final int ANIMATION_FRAME_DURATION = 1000 / 60;

	private static final int EXPANDED_FULL_OPEN = -10001;
	private static final int COLLAPSED_FULL_CLOSED = -10002;

	private final int mHandleId;
	private final int mContentId;

	private View mHandle;
	private View mContent;

	private final Rect mFrame = new Rect();
	private final Rect mInvalidate = new Rect();
	private boolean mTracking;
	private boolean mLocked;

	private VelocityTracker mVelocityTracker;

	private boolean mVertical;
	private boolean mExpanded;
	private int mBottomOffset;
	private int mTopOffset;
	private int mHandleHeight;
	private int mHandleWidth;

	private OnDrawerOpenListener mOnDrawerOpenListener;
	private OnDrawerCloseListener mOnDrawerCloseListener;
	private OnDrawerScrollListener mOnDrawerScrollListener;

	private final Handler mHandler = new SlidingHandler();
	private float mAnimatedAcceleration;
	private float mAnimatedVelocity;
	private float mAnimationPosition;
	private long mAnimationLastTime;
	private long mCurrentAnimationTime;
	private int mTouchDelta;
	private boolean mAnimating;
	private boolean mAllowSingleTap;
	private boolean mAnimateOnClick;

	private final int mTapThreshold;
	private final int mMaximumTapVelocity;
	private final int mMaximumMinorVelocity;
	private final int mMaximumMajorVelocity;
	private final int mMaximumAcceleration;
	private final int mVelocityUnits;

	// =======================add
	private int mWidthScreen;
	private int mHeightScreen;

	public void setTopOffset(int offset)
	{
		if (offset >= 0 && offset < mHeightScreen)
		{
			this.mTopOffset = offset;
		}
	}

	/**
	 * Callback invoked when the drawer is opened.
	 */
	public static interface OnDrawerOpenListener
	{
		/**
		 * Invoked when the drawer becomes fully open.
		 */
		public void onDrawerOpened();
	}

	/**
	 * Callback invoked when the drawer is closed.
	 */
	public static interface OnDrawerCloseListener
	{
		/**
		 * Invoked when the drawer becomes fully closed.
		 */
		public void onDrawerClosed();
	}

	/**
	 * Callback invoked when the drawer is scrolled.
	 */
	public static interface OnDrawerScrollListener
	{
		/**
		 * Invoked when the user starts dragging/flinging the drawer's handle.
		 */
		public void onScrollStarted();

		/**
		 * Invoked when the user stops dragging/flinging the drawer's handle.
		 */
		public void onScrollEnded();
	}

	/**
	 * Creates a new SlidingDrawer from a specified set of attributes defined in
	 * XML.
	 * 
	 * @param context
	 *            The application's environment.
	 * @param attrs
	 *            The attributes defined in XML.
	 */
	public SlidingDrawer(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);
	}

	/**
	 * Creates a new SlidingDrawer from a specified set of attributes defined in
	 * XML.
	 * 
	 * @param context
	 *            The application's environment.
	 * @param attrs
	 *            The attributes defined in XML.
	 * @param defStyle
	 *            The style to apply to this widget.
	 */
	public SlidingDrawer(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingDrawer, defStyle, 0);

		int orientation = a.getInt(R.styleable.SlidingDrawer_s_orientation, ORIENTATION_VERTICAL);
		mVertical = orientation == ORIENTATION_VERTICAL;
		mBottomOffset = (int) a.getDimension(R.styleable.SlidingDrawer_s_bottomOffset, 0.0f);
		mTopOffset = (int) a.getDimension(R.styleable.SlidingDrawer_s_topOffset, 0.0f);
		mAllowSingleTap = a.getBoolean(R.styleable.SlidingDrawer_s_allowSingleTap, true);
		mAnimateOnClick = a.getBoolean(R.styleable.SlidingDrawer_s_animateOnClick, true);

		int handleId = a.getResourceId(R.styleable.SlidingDrawer_s_handle, 0);
		if (handleId == 0)
		{
			throw new IllegalArgumentException("The handle attribute is required and must refer " + "to a valid child.");
		}
		int contentId = a.getResourceId(R.styleable.SlidingDrawer_s_content, 0);
		if (contentId == 0)
		{
			throw new IllegalArgumentException("The content attribute is required and must refer " + "to a valid child.");
		}

		if (handleId == contentId)
		{
			throw new IllegalArgumentException("The content and handle attributes must refer " + "to different children.");
		}

		mHandleId = handleId;
		mContentId = contentId;

		final float density = getResources().getDisplayMetrics().density;
		mTapThreshold = (int) (TAP_THRESHOLD * density + 0.5f);
		mMaximumTapVelocity = (int) (MAXIMUM_TAP_VELOCITY * density + 0.5f);
		mMaximumMinorVelocity = (int) (MAXIMUM_MINOR_VELOCITY * density + 0.5f);
		mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);
		mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);
		mVelocityUnits = (int) (VELOCITY_UNITS * density + 0.5f);

		// ===========================add==============
		mWidthScreen = getResources().getDisplayMetrics().widthPixels;
		mHeightScreen = getResources().getDisplayMetrics().heightPixels;

		a.recycle();

		setAlwaysDrawnWithCacheEnabled(false);
	}

	@Override
	protected void onFinishInflate()
	{
		Log.i("tag", "===========onFinishInflate===========");
		mHandle = findViewById(mHandleId);
		if (mHandle == null)
		{
			throw new IllegalArgumentException("The handle attribute is must refer to an" + " existing child.");
		}
		mHandle.setOnClickListener(new DrawerToggler());

		mContent = findViewById(mContentId);
		if (mContent == null)
		{
			throw new IllegalArgumentException("The content attribute is must refer to an" + " existing child.");
		}

		// mContent.setVisibility(View.GONE);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		Log.i("tag", "===========onMeasure===========");
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

		int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
		int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

		if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED)
		{
			throw new RuntimeException("SlidingDrawer cannot have UNSPECIFIED dimensions");
		}

		final View handle = mHandle;
		measureChild(handle, widthMeasureSpec, heightMeasureSpec);

		if (mVertical)
		{
			int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;
			mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY),
					MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
		} else
		{
			int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;
			mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
					MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY));
		}

		setMeasuredDimension(widthSpecSize, heightSpecSize);
	}

	@Override
	protected void dispatchDraw(Canvas canvas)
	{
		final long drawingTime = getDrawingTime();
		final View handle = mHandle;
		final boolean isVertical = mVertical;

		drawChild(canvas, handle, drawingTime);

		if (mTracking || mAnimating)
		{
			final Bitmap cache = mContent.getDrawingCache();
			if (cache != null)
			{
				if (isVertical)
				{
					canvas.drawBitmap(cache, 0, handle.getBottom(), null);
				} else
				{
					canvas.drawBitmap(cache, handle.getRight(), 0, null);
				}
			} else
			{
				canvas.save();
				canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset, isVertical ? handle.getTop() - mTopOffset : 0);
				drawChild(canvas, mContent, drawingTime);
				canvas.restore();
			}
		} else if (mExpanded)
		{
			drawChild(canvas, mContent, drawingTime);
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b)
	{
		Log.i("tag", "===========onLayout===========");
		if (mTracking)
		{
			return;
		}

		final int width = r - l;
		final int height = b - t;

		final View handle = mHandle;

		int childWidth = handle.getMeasuredWidth();
		int childHeight = handle.getMeasuredHeight();

		int childLeft;
		int childTop;

		final View content = mContent;

		if (mVertical)
		{
			childLeft = (width - childWidth) / 2;
			childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset;

			content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), mTopOffset + childHeight + content.getMeasuredHeight());
		} else
		{
			childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset;
			childTop = (height - childHeight) / 2;

			content.layout(mTopOffset + childWidth, 0, mTopOffset + childWidth + content.getMeasuredWidth(), content.getMeasuredHeight());
		}

		handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
		mHandleHeight = handle.getHeight();
		mHandleWidth = handle.getWidth();
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent event)
	{
		if (mLocked)
		{
			return false;
		}

		final int action = event.getAction();

		float x = event.getX();
		float y = event.getY();

		final Rect frame = mFrame;
		final View handle = mHandle;

		handle.getHitRect(frame);
		if (!mTracking && !frame.contains((int) x, (int) y))
		{
			return false;
		}

		if (action == MotionEvent.ACTION_DOWN)
		{
			mTracking = true;

			handle.setPressed(true);
			// Must be called before prepareTracking()
			prepareContent();

			// Must be called after prepareContent()
			if (mOnDrawerScrollListener != null)
			{
				mOnDrawerScrollListener.onScrollStarted();
			}

			if (mVertical)
			{
				final int top = mHandle.getTop();
				mTouchDelta = (int) y - top;
				prepareTracking(top);
			} else
			{
				final int left = mHandle.getLeft();
				mTouchDelta = (int) x - left;
				prepareTracking(left);
			}
			mVelocityTracker.addMovement(event);
		}

		return true;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		if (mLocked)
		{
			return true;
		}

		if (mTracking)
		{
			mVelocityTracker.addMovement(event);
			final int action = event.getAction();
			switch (action)
			{
			case MotionEvent.ACTION_MOVE:
				moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta);
				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_CANCEL:
			{
				final VelocityTracker velocityTracker = mVelocityTracker;
				velocityTracker.computeCurrentVelocity(mVelocityUnits);

				float yVelocity = velocityTracker.getYVelocity();
				float xVelocity = velocityTracker.getXVelocity();
				boolean negative;

				final boolean vertical = mVertical;
				if (vertical)
				{
					negative = yVelocity < 0;
					if (xVelocity < 0)
					{
						xVelocity = -xVelocity;
					}
					if (xVelocity > mMaximumMinorVelocity)
					{
						xVelocity = mMaximumMinorVelocity;
					}
				} else
				{
					negative = xVelocity < 0;
					if (yVelocity < 0)
					{
						yVelocity = -yVelocity;
					}
					if (yVelocity > mMaximumMinorVelocity)
					{
						yVelocity = mMaximumMinorVelocity;
					}
				}

				float velocity = (float) Math.hypot(xVelocity, yVelocity);
				if (negative)
				{
					velocity = -velocity;
				}

				final int top = mHandle.getTop();
				final int left = mHandle.getLeft();

				if (Math.abs(velocity) < mMaximumTapVelocity)
				{
					if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset)
							|| (!mExpanded && top > mBottomOffset + getBottom() - getTop() - mHandleHeight - mTapThreshold)
							: (mExpanded && left < mTapThreshold + mTopOffset)
									|| (!mExpanded && left > mBottomOffset + getRight() - getLeft() - mHandleWidth - mTapThreshold))
					{

						if (mAllowSingleTap)
						{
							playSoundEffect(SoundEffectConstants.CLICK);

							if (mExpanded)
							{
								animateClose(vertical ? top : left);
							} else
							{
								animateOpen(vertical ? top : left);
							}
						} else
						{
							performFling(vertical ? top : left, velocity, false);
						}

					} else
					{
						performFling(vertical ? top : left, velocity, false);
					}
				} else
				{
					performFling(vertical ? top : left, velocity, false);
				}
			}
				break;
			}
		}

		return mTracking || mAnimating || super.onTouchEvent(event);
	}

	private void animateClose(int position)
	{
		prepareTracking(position);
		performFling(position, mMaximumAcceleration, true);
	}

	private void animateOpen(int position)
	{
		prepareTracking(position);
		performFling(position, -mMaximumAcceleration, true);
	}

	private void performFling(int position, float velocity, boolean always)
	{
		mAnimationPosition = position;
		mAnimatedVelocity = velocity;

		if (mExpanded)
		{
			if (always
					|| (velocity > mMaximumMajorVelocity || (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) && velocity > -mMaximumMajorVelocity)))
			{
				// We are expanded, but they didn't move sufficiently to cause
				// us to retract. Animate back to the expanded position.
				mAnimatedAcceleration = mMaximumAcceleration;
				if (velocity < 0)
				{
					mAnimatedVelocity = 0;
				}
			} else
			{
				// We are expanded and are now going to animate away.
				mAnimatedAcceleration = -mMaximumAcceleration;
				if (velocity > 0)
				{
					mAnimatedVelocity = 0;
				}
			}
		} else
		{
			if (!always
					&& (velocity > mMaximumMajorVelocity || (position > (mVertical ? getHeight() : getWidth()) / 2 && velocity > -mMaximumMajorVelocity)))
			{
				// We are collapsed, and they moved enough to allow us to
				// expand.
				mAnimatedAcceleration = mMaximumAcceleration;
				if (velocity < 0)
				{
					mAnimatedVelocity = 0;
				}
			} else
			{
				// We are collapsed, but they didn't move sufficiently to cause
				// us to retract. Animate back to the collapsed position.
				mAnimatedAcceleration = -mMaximumAcceleration;
				if (velocity > 0)
				{
					mAnimatedVelocity = 0;
				}
			}
		}

		long now = SystemClock.uptimeMillis();
		mAnimationLastTime = now;
		mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
		mAnimating = true;
		mHandler.removeMessages(MSG_ANIMATE);
		mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
		stopTracking();
	}

	private void prepareTracking(int position)
	{
		mTracking = true;
		mVelocityTracker = VelocityTracker.obtain();
		boolean opening = !mExpanded;
		if (opening)
		{
			mAnimatedAcceleration = mMaximumAcceleration;
			mAnimatedVelocity = mMaximumMajorVelocity;
			mAnimationPosition = mBottomOffset + (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth);
			moveHandle((int) mAnimationPosition);
			mAnimating = true;
			mHandler.removeMessages(MSG_ANIMATE);
			long now = SystemClock.uptimeMillis();
			mAnimationLastTime = now;
			mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
			mAnimating = true;
		} else
		{
			if (mAnimating)
			{
				mAnimating = false;
				mHandler.removeMessages(MSG_ANIMATE);
			}
			moveHandle(position);
		}
	}

	private void moveHandle(int position)
	{
		final View handle = mHandle;

		if (mVertical)
		{
			if (position == EXPANDED_FULL_OPEN)
			{
				handle.offsetTopAndBottom(mTopOffset - handle.getTop());
				invalidate();
			} else if (position == COLLAPSED_FULL_CLOSED)
			{
				handle.offsetTopAndBottom(mBottomOffset + getBottom() - getTop() - mHandleHeight - handle.getTop());
				invalidate();
			} else
			{
				final int top = handle.getTop();
				int deltaY = position - top;
				if (position < mTopOffset)
				{
					deltaY = mTopOffset - top;
				} else if (deltaY > mBottomOffset + getBottom() - getTop() - mHandleHeight - top)
				{
					deltaY = mBottomOffset + getBottom() - getTop() - mHandleHeight - top;
				}
				handle.offsetTopAndBottom(deltaY);

				final Rect frame = mFrame;
				final Rect region = mInvalidate;

				handle.getHitRect(frame);
				region.set(frame);

				region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY);
				region.union(0, frame.bottom - deltaY, getWidth(), frame.bottom - deltaY + mContent.getHeight());

				invalidate(region);
			}
		} else
		{
			if (position == EXPANDED_FULL_OPEN)
			{
				handle.offsetLeftAndRight(mTopOffset - handle.getLeft());
				invalidate();
			} else if (position == COLLAPSED_FULL_CLOSED)
			{
				handle.offsetLeftAndRight(mBottomOffset + getRight() - getLeft() - mHandleWidth - handle.getLeft());
				invalidate();
			} else
			{
				final int left = handle.getLeft();
				int deltaX = position - left;
				if (position < mTopOffset)
				{
					deltaX = mTopOffset - left;
				} else if (deltaX > mBottomOffset + getRight() - getLeft() - mHandleWidth - left)
				{
					deltaX = mBottomOffset + getRight() - getLeft() - mHandleWidth - left;
				}
				handle.offsetLeftAndRight(deltaX);

				final Rect frame = mFrame;
				final Rect region = mInvalidate;

				handle.getHitRect(frame);
				region.set(frame);

				region.union(frame.left - deltaX, frame.top, frame.right - deltaX, frame.bottom);
				region.union(frame.right - deltaX, 0, frame.right - deltaX + mContent.getWidth(), getHeight());

				invalidate(region);
			}
		}
	}

	private void prepareContent()
	{
		if (mAnimating)
		{
			return;
		}

		// Something changed in the content, we need to honor the layout request
		// before creating the cached bitmap
		final View content = mContent;
		if (content.isLayoutRequested())
		{
			if (mVertical)
			{
				final int childHeight = mHandleHeight;
				int height = getBottom() - getTop() - childHeight - mTopOffset;
				content.measure(MeasureSpec.makeMeasureSpec(getRight() - getLeft(), MeasureSpec.EXACTLY),
						MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
				content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), mTopOffset + childHeight + content.getMeasuredHeight());
			} else
			{
				final int childWidth = mHandle.getWidth();
				int width = getRight() - getLeft() - childWidth - mTopOffset;
				content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
						MeasureSpec.makeMeasureSpec(getBottom() - getTop(), MeasureSpec.EXACTLY));
				content.layout(childWidth + mTopOffset, 0, mTopOffset + childWidth + content.getMeasuredWidth(), content.getMeasuredHeight());
			}
		}
		// Try only once... we should really loop but it's not a big deal
		// if the draw was cancelled, it will only be temporary anyway
		content.getViewTreeObserver().dispatchOnPreDraw();
		content.buildDrawingCache();

		// content.setVisibility(View.GONE);
	}

	private void stopTracking()
	{
		mHandle.setPressed(false);
		mTracking = false;

		if (mOnDrawerScrollListener != null)
		{
			mOnDrawerScrollListener.onScrollEnded();
		}

		if (mVelocityTracker != null)
		{
			mVelocityTracker.recycle();
			mVelocityTracker = null;
		}
	}

	private void doAnimation()
	{
		if (mAnimating)
		{
			incrementAnimation();
			if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1)
			{
				mAnimating = false;
				closeDrawer();
			} else if (mAnimationPosition < mTopOffset)
			{
				mAnimating = false;
				openDrawer();
			} else
			{
				moveHandle((int) mAnimationPosition);
				mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
				mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
			}
		}
	}

	private void incrementAnimation()
	{
		long now = SystemClock.uptimeMillis();
		float t = (now - mAnimationLastTime) / 1000.0f; // ms -> s
		final float position = mAnimationPosition;
		final float v = mAnimatedVelocity; // px/s
		final float a = mAnimatedAcceleration; // px/s/s
		mAnimationPosition = position + (v * t) + (0.5f * a * t * t); // px
		mAnimatedVelocity = v + (a * t); // px/s
		mAnimationLastTime = now; // ms
	}

	/**
	 * Toggles the drawer open and close. Takes effect immediately.
	 * 
	 * @see #open()
	 * @see #close()
	 * @see #animateClose()
	 * @see #animateOpen()
	 * @see #animateToggle()
	 */
	public void toggle()
	{
		if (!mExpanded)
		{
			openDrawer();
		} else
		{
			closeDrawer();
		}
		invalidate();
		requestLayout();
	}

	/**
	 * Toggles the drawer open and close with an animation.
	 * 
	 * @see #open()
	 * @see #close()
	 * @see #animateClose()
	 * @see #animateOpen()
	 * @see #toggle()
	 */
	public void animateToggle()
	{
		if (!mExpanded)
		{
			animateOpen();
		} else
		{
			animateClose();
		}
	}

	/**
	 * Opens the drawer immediately.
	 * 
	 * @see #toggle()
	 * @see #close()
	 * @see #animateOpen()
	 */
	public void open()
	{
		openDrawer();
		invalidate();
		requestLayout();

		sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
	}

	/**
	 * Closes the drawer immediately.
	 * 
	 * @see #toggle()
	 * @see #open()
	 * @see #animateClose()
	 */
	public void close()
	{
		closeDrawer();
		invalidate();
		requestLayout();
	}

	/**
	 * Closes the drawer with an animation.
	 * 
	 * @see #close()
	 * @see #open()
	 * @see #animateOpen()
	 * @see #animateToggle()
	 * @see #toggle()
	 */
	public void animateClose()
	{
		prepareContent();
		final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
		if (scrollListener != null)
		{
			scrollListener.onScrollStarted();
		}
		animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft());

		if (scrollListener != null)
		{
			scrollListener.onScrollEnded();
		}
	}

	/**
	 * Opens the drawer with an animation.
	 * 
	 * @see #close()
	 * @see #open()
	 * @see #animateClose()
	 * @see #animateToggle()
	 * @see #toggle()
	 */
	public void animateOpen()
	{
		prepareContent();
		final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
		if (scrollListener != null)
		{
			scrollListener.onScrollStarted();
		}
		animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());

		sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);

		if (scrollListener != null)
		{
			scrollListener.onScrollEnded();
		}
	}

	private void closeDrawer()
	{
		moveHandle(COLLAPSED_FULL_CLOSED);
		// mContent.setVisibility(View.GONE);
		mContent.destroyDrawingCache();

		if (!mExpanded)
		{
			return;
		}

		mExpanded = false;
		if (mOnDrawerCloseListener != null)
		{
			mOnDrawerCloseListener.onDrawerClosed();
		}
	}

	private void openDrawer()
	{
		moveHandle(EXPANDED_FULL_OPEN);
		// mContent.setVisibility(View.VISIBLE);

		if (mExpanded)
		{
			return;
		}

		mExpanded = true;

		if (mOnDrawerOpenListener != null)
		{
			mOnDrawerOpenListener.onDrawerOpened();
		}
	}

	/**
	 * Sets the listener that receives a notification when the drawer becomes
	 * open.
	 * 
	 * @param onDrawerOpenListener
	 *            The listener to be notified when the drawer is opened.
	 */
	public void setOnDrawerOpenListener(OnDrawerOpenListener onDrawerOpenListener)
	{
		mOnDrawerOpenListener = onDrawerOpenListener;
	}

	/**
	 * Sets the listener that receives a notification when the drawer becomes
	 * close.
	 * 
	 * @param onDrawerCloseListener
	 *            The listener to be notified when the drawer is closed.
	 */
	public void setOnDrawerCloseListener(OnDrawerCloseListener onDrawerCloseListener)
	{
		mOnDrawerCloseListener = onDrawerCloseListener;
	}

	/**
	 * Sets the listener that receives a notification when the drawer starts or
	 * ends a scroll. A fling is considered as a scroll. A fling will also
	 * trigger a drawer opened or drawer closed event.
	 * 
	 * @param onDrawerScrollListener
	 *            The listener to be notified when scrolling starts or stops.
	 */
	public void setOnDrawerScrollListener(OnDrawerScrollListener onDrawerScrollListener)
	{
		mOnDrawerScrollListener = onDrawerScrollListener;
	}

	/**
	 * Returns the handle of the drawer.
	 * 
	 * @return The View reprenseting the handle of the drawer, identified by the
	 *         "handle" id in XML.
	 */
	public View getHandle()
	{
		return mHandle;
	}

	/**
	 * Returns the content of the drawer.
	 * 
	 * @return The View reprenseting the content of the drawer, identified by
	 *         the "content" id in XML.
	 */
	public View getContent()
	{
		return mContent;
	}

	/**
	 * Unlocks the SlidingDrawer so that touch events are processed.
	 * 
	 * @see #lock()
	 */
	public void unlock()
	{
		mLocked = false;
	}

	/**
	 * Locks the SlidingDrawer so that touch events are ignores.
	 * 
	 * @see #unlock()
	 */
	public void lock()
	{
		mLocked = true;
	}

	/**
	 * Indicates whether the drawer is currently fully opened.
	 * 
	 * @return True if the drawer is opened, false otherwise.
	 */
	public boolean isOpened()
	{
		return mExpanded;
	}

	/**
	 * Indicates whether the drawer is scrolling or flinging.
	 * 
	 * @return True if the drawer is scroller or flinging, false otherwise.
	 */
	public boolean isMoving()
	{
		return mTracking || mAnimating;
	}

	private class DrawerToggler implements OnClickListener
	{
		public void onClick(View v)
		{
			if (mLocked)
			{
				return;
			}
			// mAllowSingleTap isn't relevant here; you're *always*
			// allowed to open/close the drawer by clicking with the
			// trackball.

			if (mAnimateOnClick)
			{
				animateToggle();
			} else
			{
				toggle();
			}
		}
	}

	private class SlidingHandler extends Handler
	{
		public void handleMessage(Message m)
		{
			switch (m.what)
			{
			case MSG_ANIMATE:
				doAnimation();
				break;
			}
		}
	}
}
